/* ----------------------------- Noble Steed Engine----------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 29 2013

File:
	nsengine.h

Description:
	This file contains the NSEngine class which is the base container for everything in the
	engine
*---------------------------------------------------------------------------*/

#ifndef NSENGINE_H
#define NSENGINE_H

#include <nsmath3d.h>
#include <nslight.h>
#include <nsmaparea.h>
#include <nsobjectset.h>
#include <nsshaderset.h>
#include <Windows.h>

#include <nscamera.h>
#include <nsframebufferobject.h>
#include <nsshader.h>
#include <nsobject.h>
#include <nsobjectreference.h>
#include <nsselectionmodel.h>
#include <nsmapbuilder.h>

class NSShaderSet;
class NSObjectSet;

class NSEngine
{
public:
	typedef NSGBuffer::ObjectIndex ObjectIndex;

	NSEngine();
	~NSEngine();

	void addObject(NSShader* shader, NSObject *object);
	NSObject * addObject(NSObject::ObjectType type, 
		NSShader* shader, 
		const std::string & name, 
		const std::string & mfileName="", 
		const std::string & mDirectory="", 
		const std::string & texDir="");

	void addReference(NSObjectReference* toAdd);
	NSObjectReference * addReference(
		NSObject * baseObj, 
		const NSVec3Df & position = NSVec3Df(),
		const NSVec3Df & orientation = NSVec3Df(), 
		const NSVec3Df & scale = NSVec3Df(1.0f,1.0f,1.0f));
	NSObjectReference * addReference(
		const std::string & objectName, 
		const NSVec3Df & position = NSVec3Df(),
		const NSVec3Df & orientation = NSVec3Df(), 
		const NSVec3Df & scale = NSVec3Df(1.0f,1.0f,1.0f));

	void addShader(NSShader * shader);
	NSShader * addShader(NSShader::ShaderProgType type, 
		const std::string & _shaderID, 
		const std::string & fragmentFileName, 
		const std::string & vertexFileName, 
		const std::string & geometryFileName = "", 
		const std::string & shaderDirectory = "Shaders");

	void addCamera(NSCamera * cam);
	NSCamera * addCamera( const std::string & camID, 
		NSCamera::CameraMode mode = NSCamera::FOCUS,
		float _speed=10.0f,
		float sens=0.1f,
		float zoomFac=5.0f,
		const NSVec3Df & pos=NSVec3Df(),
		const NSVec3Df & orient=NSVec3Df(),
		const NSVec3Df & rotPoint=NSVec3Df(),
		const NSMatrix3Df & startingOrientation = NSMatrix3Df());

	void clearMap();
	NSObjectReference * createReference(
		NSObject * baseObj, 
		const NSVec3Df & position = NSVec3Df(),
		const NSVec3Df & orientation = NSVec3Df(), 
		const NSVec3Df & scale = NSVec3Df(1.0f,1.0f,1.0f));

	void deserializeMap(std::ifstream & in);
	void deserializeObjects( std::ifstream & in);
	void deserializeShaders( std::ifstream & ni);
	void draw();
	void drawOverlays();

	bool isOnScreen(const NSVec3Df & worldPos);

	NSCamera * getCamera();
	NSMatrix4Df getCameraMatrix();
	NSMatrix4Df getInverseProjectionMatrix();
	NSMapBuilder * getMapBuilder();
	NSMapArea::MapSize getMapSize();
	NSVec2Df getNDCFromMousePos(const NSVec2Df & mousePos);
	NSVec2Df getNDCFromMousePos(float xPos, float yPos);
	NSObject * getObject(const std::string & objectName);
	NSObject * getObject(unsigned int objectID);
	ObjectIndex getObjectIndex(const NSVec2Df & mousePos);
	ObjectIndex getObjectIndex(int xMousePos, int yMousePos);
	NSMatrix4Df getProjectionMatrix();
	NSObjectReference * getReference(unsigned int refID, NSObject * baseObj);
	unsigned int getReferenceCount(NSObject * baseObj);
	NSVec3Df getScreenCenterWolrdPos(float depthFromCam = 10.0f);
	NSVec3Df getScreenSpace(const NSVec3Df & worldSpace);
	NSSelectionModel * getSelectionModel();
	NSShader * getShader(unsigned int progID);
	NSShader * getShader(const std::string & shaderID);
	bool getShowOccupiedTiles() const;
	unsigned int getTotalReferenceCount();

	int height();

	void redrawObjectsNoColorBuffer(const NSMatrix4Df & projCamMat);
	bool removeCamera();
	bool removeObject(const std::string & objectName);
	bool removeObject(NSObject *object);
	bool removeObjectsShader(NSShader * toRemove);
	bool removeAllReferencesOf(NSObject * base);
	bool removeReference(unsigned int referenceID, NSObject * baseObj);
	bool removeReference(NSObjectReference* reference);
	bool removeShader(NSShader * shader);
	bool removeShader(unsigned int progID);
	bool removeShader(const std::string & shaderID);
	void resize(int width, int height);
	void resizeBuffer(NSFrameBufferObject::BufferType which, int width, int height);

	void serializeMap(std::ofstream & out);
	void serializeObjects( std::ofstream & out);
	void serializeShaders( std::ofstream & out);
	void setAllShaderUniforms(const char * variableName, const NSMatrix4Df & data) const;
	void setAllShaderUniforms(const char * variableName, const NSMatrix3Df & data) const;
	void setAllShaderUniforms(const char * variableName, const NSVec4Df & data) const;
	void setAllShaderUniforms(const char * variableName, const NSVec3Df & data) const;
	void setAllShaderUniforms(const char * variableName, const NSVec2Df & data) const;
	void setAllShaderUniforms(const char * variableName, float dat) const;
	void setAllShaderUniforms(const char * variableName, int data) const;
	void setAllShaderUniforms(const char * variableName, unsigned int data) const;

	void setDirectionalLight(NSDirectionLight * dLight);
	void setDirectionalLight( 
		const std::string & name, 
		const NSVec3Df & _direction = NSVec3Df(0.0f,0.0f,-1.0f), 
		float diffuse = 0.3f, 
		float ambient = 0.1f,
		float specularPower = 0.0f,
		float specularIntensity = 0.0f,
		const NSVec3Df & _color = NSVec3Df(1.0f,1.0f,1.0f));

	void setProjectionMatrix(float FOVAngle, 
		int width, 
		int height, 
		float zNear=1.0f, 
		float zFar=1000.0f);
	void setShowOccupiedTiles(bool show);

	void update();
	int width();

	NSMapArea::Iterator mapBegin();
	NSMapArea::Iterator mapEnd();
	NSObjectSet::Iterator objectsMapBegin();
	NSObjectSet::Iterator objectsMapEnd();
	NSObjectSet::ObjectIterator objectsBegin( NSShader *shader );
	NSObjectSet::ObjectIterator objectsEnd( NSShader * shader );
	NSMapArea::ObjectIterator objectsRefSetBegin(NSObject::ObjectType whichType);
	NSMapArea::ObjectIterator objectsRefSetEnd(NSObject::ObjectType whichType);
	NSMapArea::RefIterator refsBegin(NSObject * baseObj);
	NSMapArea::RefIterator refsEnd(NSObject * baseObj);
	NSShaderSet::Iterator shadersBegin();
	NSShaderSet::Iterator shadersEnd();
	bool pu;

private:
	struct ProjectionInfo
	{
		ProjectionInfo();

		float FOVAngle;
		int width;
		int height;
		float zNear;
		float zFar;
	};

	NSMapArea * mapArea;
	NSMapBuilder * mapBuilder;
	NSObjectSet * objects;
	NSShaderSet * shaders;

	bool showOccupiedTiles;

	NSCamera* camera;
	std::vector<NSFrameBufferObject*> frameBuffers;

	ProjectionInfo projInfo;
	NSMatrix4Df projMat;
	NSMatrix4Df inverseProjMat;
};

#endif